/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/** 
 * \file
 * \brief HubCtrl common parts - implementation.
 * \author Antti Maula <antti.maula@tkk.fi>
 */

//*****************************************************************************
#include "hubctrl_common.h"
#include "apsecurity.h"
#include "owndebug.h"
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

int hubctrlcommonUpdateMessageAuthentication(uint8_t *message,
                                             const unsigned int messagelen,
                                             THubCtrlSubHeaderAuthentication *auth,
                                             const uint8_t *key,
                                             const unsigned int keylen)
{
  return -1;
}
//*****************************************************************************

int hubctrlcommonAuthenticateMessage(const uint8_t *message,
                                     const unsigned int messagelen,
                                     const THubCtrlSubHeaderAuthentication *auth,
                                     const uint8_t *key,
                                     const unsigned int keylen,
                                     const unsigned int time_diff_max)
{
  // Allocate!
  uint8_t msg_sha1[sizeof(auth->sha1)];
  uint8_t calc_sha1[sizeof(auth->sha1)];
  unsigned int calc_sha1_size = sizeof(auth->sha1);
  int result = 1;

  // Check timestamp validity
  const time_t now = time(NULL);
  if ((now - time_diff_max) < auth->timestamp_s &&
      (now + time_diff_max) > auth->timestamp_s) {
    //    dPrint(ODTEST,"Timestamp valid");
    
    // Copy!
    memcpy(msg_sha1, auth->sha1, sizeof(auth->sha1));
    
    // Zero! (typecasting required to override constness)
    memset((void*)auth->sha1, 0x00, sizeof(auth->sha1));
    
    // Calculate!
    apsecurityCalculateHMAC(calc_sha1,
                            &calc_sha1_size,
                            (void*)key,
                            keylen,
                            message,
                            messagelen);
    
    // Write back!
    memcpy((void*)auth->sha1, msg_sha1, sizeof(auth->sha1));

    // Compare!
    result = memcmp(msg_sha1, calc_sha1, sizeof(auth->sha1));
    //dPrint(ODTEST,"Comparing regions yields %d", result);

    // Modify to match specs! (If not Zero == Change to failure)
    if (result) result = -1;

  } else {
    dPrint(ODWARN,"Received Message with invalid Timestamp! (Timestamp_s = %u, while now_s = %u)",
           auth->timestamp_s, now);

  }
  
  // return!
  return result;
}
//*****************************************************************************

int hubctrlcommonGetSubHeaders(const THubCtrlProtocolHeader *hdr,
                               const unsigned int total_bytes,
                               THubCtrlSubHeaderIdentificationHeader ***hdrppp,
                               unsigned int *hdrcount)
{
  int result = -1;
  THubCtrlSubHeaderIdentificationHeader **outputHdrPtr = NULL;
  
  
  // Does the packet contain subheaders and if; does it have enough data?
  if (hdr->subheader_count > 0) {

    if (total_bytes > ( sizeof(THubCtrlProtocolHeader) + 
                        sizeof(THubCtrlSubHeaderIdentificationHeader)) ) {
      
      // We have subheaders, and the received data is bigger than header only. Walk!
      uint8_t *subdata = (uint8_t*)(hdr+1);
      int current_byte = 0;
      int bytes_left = total_bytes - sizeof(THubCtrlProtocolHeader);
      unsigned int processed_subhdrs = 0;
      
      // Alloc memory for hdrppp if set.
      if (hdrppp){ 
        const unsigned int hdrtablesize = ( hdr->subheader_count * 
                                            sizeof(THubCtrlSubHeaderIdentificationHeader *) );
        *hdrppp = (THubCtrlSubHeaderIdentificationHeader **)malloc(hdrtablesize);
        assert(*hdrppp != NULL);
        memset(*hdrppp, 0x00, hdrtablesize);
        *hdrcount = 0;
        outputHdrPtr = *hdrppp;
      }
      
      
      // Do While there is _more_ than ID. (More, because id alone is not good for anything)
      while(bytes_left > sizeof(THubCtrlSubHeaderIdentificationHeader)) {
        THubCtrlSubHeaderIdentificationHeader *idh = 
          (THubCtrlSubHeaderIdentificationHeader *)(subdata+current_byte);
        const unsigned int subframe_size = idh->subsize;
        
        if (subframe_size <= bytes_left) {
        
          // Store
          if (hdrppp) { 
            *outputHdrPtr = idh;
            outputHdrPtr++;
          }
        
          current_byte += subframe_size;
          bytes_left -= subframe_size;
          ++processed_subhdrs;
        } else {
          break;

        }
      }
      
      // Now, check that processed directions count reached promised number of headers.
      if (processed_subhdrs == hdr->subheader_count) {
        // Match.
        if (hdrcount) *hdrcount = processed_subhdrs;
        result = 0;

      } else {
        free(*hdrppp);
        *hdrppp = NULL;

      }
    
    } else {
      // Claims to have subheaders but doesn't have enough data!
      // Do nothing. Nothing allocated,
    }
  } else {
    // No subheaders, hence no subheaders but still OK.
    result = 0;
    
  }
  
  return result;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
